package openglsuperbible.Chapter4;


import openglsuperbible.glutils.*;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.*;
import static org.lwjgl.opengl.GL11.*;



public class ModelviewProjection {
        public static final int DISPLAY_HEIGHT = 600;
        public static final int DISPLAY_WIDTH = 800;
        private static final int MATRIX_SIZE = 16;
        
        // Global view frustum class
        private GLFrustrum frustrum;
        
        private GLShader shader;        
        
        private GLBatch torusBatch;     

        // Matrix Variables

        float[] mTranslate = new float[MATRIX_SIZE];
        float[] mRotate = new float[MATRIX_SIZE];
        float[] mModelview = new float[MATRIX_SIZE];
        float[] mModelViewProjection = new float[MATRIX_SIZE];

        float angle = 0.0f;

        float vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
        
        
        /** time at last frame */
	long lastFrame;
        
            public void initGL() {   
                glClearColor(0.8f, 0.8f, 0.8f, 1.0f );
                shader = GLShaderFactory.getFlatShader();
                glEnable(GL_DEPTH_TEST);
                glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
                frustrum = new GLFrustrum();
                torusBatch = GLBatchFactory.makeTorus(0.4f, 0.15f, 30, 30);
              
            }   
        
            public void resizeGL() {   
                glViewport(0,0,DISPLAY_WIDTH ,DISPLAY_HEIGHT);
                frustrum.setPerspective(35.0f, Display.getWidth()/ Display.getHeight(),1.0f, 1000.0f);
             } 
            
            

            
            public void render() {
                angle+= 0.1f * getDelta();
                glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

                shader.useShader(); 

                shader.setUniform4("vColor",vBlack);
                
                // Create a translation matrix to move the torus back and into sight
                Math3D.translationMatrix44f(mTranslate, 0, 0, -2.5f);
                // Create a rotation matrix based on the current value of angle
                Math3D.rotationMatrix44(mRotate,(float) Math.toRadians(angle), 0, 1, 0);
                // Add the rotation to the translation, store the result in mModelView
                Math3D.matrixMultiply44(mModelview, mTranslate, mRotate);
                // Add the modelview matrix to the projection matrix, 
                // the final matrix is the ModelViewProjection matrix.
                Math3D.matrixMultiply44(mModelViewProjection, frustrum.getProjectionMatrix(),mModelview);
                // Pass this completed matrix to the shader, and render the torus
                shader.setUniformMatrix4("mvpMatrix", false, mModelViewProjection);                
                torusBatch.draw(shader.getAttributeLocations());   
                
                Display.update();  
            }  
            
        
        
       public void create() throws LWJGLException { 
            //Display    
            Display.setDisplayMode(new DisplayMode(DISPLAY_WIDTH,DISPLAY_HEIGHT)); 
            Display.setFullscreen(false);
            Display.setTitle("ModelviewProjection");
            Display.create(new PixelFormat(), new ContextAttribs(3, 2).withProfileCompatibility(true));
            //Keyboard         
            Keyboard.create();   
            //Mouse          
            Mouse.setGrabbed(false);
            Mouse.create();  
            //OpenGL      
            initGL();    
            resizeGL();  
        }  
       
       
       
            public void destroy() {  
                //Methods already check if created before destroying.  
                Mouse.destroy();   
                Keyboard.destroy();
                // Display.destroy();     
            } 
        
            public static void main(String[] args) {
            ModelviewProjection main = null;
            try { 
                 main = new ModelviewProjection(); 
                main.create(); 
                main.run();
            }          
            catch(Exception ex) {  
                 
            }          
            finally { 
                if(main != null) {  
                    main.destroy();
                }         
            }      
        } 
            
                        public void run() 
            {
                while (!Display.isCloseRequested() && !Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {              
                    if (Display.isVisible()) 
                    { 
                        render();
                    }
                    else 
                    {     
                        if (Display.isDirty()) {
                            render();
                        }
                    }          
            //Display.update();     
                Display.sync(60);
                Display.setVSyncEnabled(true); 
                }                
                try{       
                    Thread.sleep(100); 
                } 
                catch (InterruptedException ex) {  }
             
            }
                        
                            	/** 
	 * Calculate how many milliseconds have passed 
	 * since last frame.
	 * 
	 * @return milliseconds passed since last frame 
	 */
	public int getDelta() {
	    long time = getTime();
	    int delta = (int) (time - lastFrame);
	    lastFrame = time;
	 
	    return delta;
	}
	
	/**
	 * Get the accurate system time
	 * 
	 * @return The system time in milliseconds
	 */
	public long getTime() {
	    return (Sys.getTime() * 1000) / Sys.getTimerResolution();
	}
}
